<!DOCTYPE html>
<html>
<head>
  <script src="../../processing.js"></script>
<link rel="stylesheet" href="../style.css"/></head>
<body><h1><a href="http://ejohn.org/blog/processingjs/">Processing.js</a></h1>
<h2>Reflection2</h2>

<p>by Ira Greenberg. 

Based on Keith Peter's Solution in
Foundation Actionscript Animation: Making Things Move!
http://www.friendsofed.com/book.html?isbn=1590597915</p>

<p><a href="http://processing.org/learning/topics/reflection2.html"><b>Original Processing.org Example:</b> Reflection2</a><br>
<script type="application/processing">
Orb orb;
Vect2D velocity;
float gravity = .05, damping = 0.8;
int segments = 40;
Ground[] ground = new Ground[segments];
float[] peakHeights = new float[segments+1];

void setup(){
  size(200, 200);
  smooth();
  orb = new Orb(50, 50, 3);
  velocity = new Vect2D(.5, 0);

  // calculate ground peak heights 
  for (int i=0; i<peakHeights.length; i++){
    peakHeights[i] = random(height-40, height-30);
  }

  /* float value required for segment width (segs)
   calculations so the ground spans the entire 
   display window, regardless of segment number. */
  float segs = segments;
  for (int i=0; i<segments; i++){
    ground[i]  = new Ground(width/segs*i, peakHeights[i],
    width/segs*(i+1), peakHeights[i+1]);
  }
}


void draw(){
  // background
  noStroke();
  fill(0, 15);
  rect(0, 0, width, height);

  // move orb
  orb.x += velocity.vx;
  velocity.vy += gravity;
  orb.y += velocity.vy;

  // draw ground
  fill(127);
  beginShape();
  for (int i=0; i<segments; i++){
    vertex(ground[i].x1, ground[i].y1);
    vertex(ground[i].x2, ground[i].y2);
  }
  vertex(ground[segments-1].x2, height);
  vertex(ground[0].x1, height);
  endShape(CLOSE);

  // draw orb
  noStroke();
  fill(200);
  ellipse(orb.x, orb.y, orb.r*2, orb.r*2);

  // collision detection
  checkWallCollision();
  for (int i=0; i<segments; i++){
    checkGroundCollision(ground[i]);
  }
}


void checkWallCollision(){
  if (orb.x > width-orb.r){
    orb.x = width-orb.r;
    velocity.vx *= -1;
    velocity.vx *= damping;
  } 
  else if (orb.x < orb.r){
    orb.x = orb.r;
    velocity.vx *= -1;
    velocity.vx *= damping;
  }
}


void checkGroundCollision(Ground groundSegment) {

  // get difference between orb and ground
  float deltaX = orb.x - groundSegment.x;
  float deltaY = orb.y - groundSegment.y;

  // precalculate trig values
  float cosine = cos(groundSegment.rot);
  float sine = sin(groundSegment.rot);

  /* rotate ground and velocity to allow 
   orthogonal collision calculations */
  float groundXTemp = cosine * deltaX + sine * deltaY;
  float groundYTemp = cosine * deltaY - sine * deltaX;
  float velocityXTemp = cosine * velocity.vx + sine * velocity.vy;
  float velocityYTemp = cosine * velocity.vy - sine * velocity.vx;

  /* ground collision - check for surface 
   collision and also that orb is within 
   left/rights bounds of ground segment */
  if (groundYTemp > -orb.r &&
    orb.x > groundSegment.x1 &&
    orb.x < groundSegment.x2 ){
    // keep orb from going into ground
    groundYTemp = -orb.r;
    // bounce and slow down orb
    velocityYTemp *= -1.0;
    velocityYTemp *= damping;
  }

  // reset ground, velocity and orb
  deltaX = cosine * groundXTemp - sine * groundYTemp;
  deltaY = cosine * groundYTemp + sine * groundXTemp;
  velocity.vx = cosine * velocityXTemp - sine * velocityYTemp;
  velocity.vy = cosine * velocityYTemp + sine * velocityXTemp;
  orb.x = groundSegment.x + deltaX;
  orb.y = groundSegment.y + deltaY;
}


class Ground {
  float x1, y1, x2, y2;  
  float x, y, len, rot;

  // default constructor
  Ground(){
  }

  // constructor
  Ground(float x1, float y1, float x2, float y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    x = (x1+x2)/2;
    y = (y1+y2)/2;
    len = dist(x1, y1, x2, y2);
    rot = atan2((y2-y1), (x2-x1));
  }
}


class Orb{
  float x, y, r;

  // default constructor
  Orb() {
  }

  Orb(float x, float y, float r) {
    this.x = x;
    this.y = y;
    this.r = r;
  }
}

class Vect2D{
  float vx, vy;

  // default constructor
  Vect2D() {
  }

  Vect2D(float vx, float vy) {
    this.vx = vx;
    this.vy = vy;
  }
}
</script><canvas width="200" height="200"></canvas></p>
<div style="height:0px;width:0px;overflow:hidden;"></div>

<pre><b>// All Examples Written by <a href="http://reas.com/">Casey Reas</a> and <a href="http://benfry.com/">Ben Fry</a>
// unless otherwise stated.</b>
Orb orb;
Vect2D velocity;
float gravity = .05, damping = 0.8;
int segments = 40;
Ground[] ground = new Ground[segments];
float[] peakHeights = new float[segments+1];

void setup(){
  size(200, 200);
  smooth();
  orb = new Orb(50, 50, 3);
  velocity = new Vect2D(.5, 0);

  // calculate ground peak heights 
  for (int i=0; i&lt;peakHeights.length; i++){
    peakHeights[i] = random(height-40, height-30);
  }

  /* float value required for segment width (segs)
   calculations so the ground spans the entire 
   display window, regardless of segment number. */
  float segs = segments;
  for (int i=0; i&lt;segments; i++){
    ground[i]  = new Ground(width/segs*i, peakHeights[i],
    width/segs*(i+1), peakHeights[i+1]);
  }
}


void draw(){
  // background
  noStroke();
  fill(0, 15);
  rect(0, 0, width, height);

  // move orb
  orb.x += velocity.vx;
  velocity.vy += gravity;
  orb.y += velocity.vy;

  // draw ground
  fill(127);
  beginShape();
  for (int i=0; i&lt;segments; i++){
    vertex(ground[i].x1, ground[i].y1);
    vertex(ground[i].x2, ground[i].y2);
  }
  vertex(ground[segments-1].x2, height);
  vertex(ground[0].x1, height);
  endShape(CLOSE);

  // draw orb
  noStroke();
  fill(200);
  ellipse(orb.x, orb.y, orb.r*2, orb.r*2);

  // collision detection
  checkWallCollision();
  for (int i=0; i&lt;segments; i++){
    checkGroundCollision(ground[i]);
  }
}


void checkWallCollision(){
  if (orb.x &gt; width-orb.r){
    orb.x = width-orb.r;
    velocity.vx *= -1;
    velocity.vx *= damping;
  } 
  else if (orb.x &lt; orb.r){
    orb.x = orb.r;
    velocity.vx *= -1;
    velocity.vx *= damping;
  }
}


void checkGroundCollision(Ground groundSegment) {

  // get difference between orb and ground
  float deltaX = orb.x - groundSegment.x;
  float deltaY = orb.y - groundSegment.y;

  // precalculate trig values
  float cosine = cos(groundSegment.rot);
  float sine = sin(groundSegment.rot);

  /* rotate ground and velocity to allow 
   orthogonal collision calculations */
  float groundXTemp = cosine * deltaX + sine * deltaY;
  float groundYTemp = cosine * deltaY - sine * deltaX;
  float velocityXTemp = cosine * velocity.vx + sine * velocity.vy;
  float velocityYTemp = cosine * velocity.vy - sine * velocity.vx;

  /* ground collision - check for surface 
   collision and also that orb is within 
   left/rights bounds of ground segment */
  if (groundYTemp &gt; -orb.r &&
    orb.x &gt; groundSegment.x1 &&
    orb.x &lt; groundSegment.x2 ){
    // keep orb from going into ground
    groundYTemp = -orb.r;
    // bounce and slow down orb
    velocityYTemp *= -1.0;
    velocityYTemp *= damping;
  }

  // reset ground, velocity and orb
  deltaX = cosine * groundXTemp - sine * groundYTemp;
  deltaY = cosine * groundYTemp + sine * groundXTemp;
  velocity.vx = cosine * velocityXTemp - sine * velocityYTemp;
  velocity.vy = cosine * velocityYTemp + sine * velocityXTemp;
  orb.x = groundSegment.x + deltaX;
  orb.y = groundSegment.y + deltaY;
}


class Ground {
  float x1, y1, x2, y2;  
  float x, y, len, rot;

  // default constructor
  Ground(){
  }

  // constructor
  Ground(float x1, float y1, float x2, float y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    x = (x1+x2)/2;
    y = (y1+y2)/2;
    len = dist(x1, y1, x2, y2);
    rot = atan2((y2-y1), (x2-x1));
  }
}


class Orb{
  float x, y, r;

  // default constructor
  Orb() {
  }

  Orb(float x, float y, float r) {
    this.x = x;
    this.y = y;
    this.r = r;
  }
}

class Vect2D{
  float vx, vy;

  // default constructor
  Vect2D() {
  }

  Vect2D(float vx, float vy) {
    this.vx = vx;
    this.vy = vy;
  }
}</pre>
</body>
</html>
